mod instance;
mod command;
mod statement;

pub use statement::*;

use std::{collections::HashMap, path::Path, str::FromStr};

pub struct Netlist {
    pub statements: Vec<Statement>
}

impl Netlist {
    pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
        match std::fs::read_to_string(path) {
            Ok(input) => { 
                let mut n = input.parse::<Self>()?;
                n.reduce_parallel_capacitance();
                Ok(n)
            },
            Err(err) => Err(format!("Read file failed for {}", err.to_string()))
        }
    }
}

impl Netlist {
    pub fn node_maping(&self) -> HashMap<String, usize> {
        let mut mapping = HashMap::<String, usize>::new();
        mapping.insert("0".to_string(), 0);
        let mut try_add_node = |node: &str| {
            if let None = mapping.get(node) {
                mapping.insert(node.to_string(), mapping.len());
            }
        };
        
        for statement in self.statements.iter() {
            match statement {
                Statement::Instance(inst) => {
                    match inst {
                        Instance::R(e) => {
                            try_add_node(&e.node1);
                            try_add_node(&e.node2);
                        }
                        Instance::L(e) => {
                            try_add_node(&e.node1);
                            try_add_node(&e.node2);
                        }
                        Instance::C(e) => {
                            try_add_node(&e.node1);
                            try_add_node(&e.node2);
                        }
                        Instance::V(e) => {
                            try_add_node(&e.node1);
                            try_add_node(&e.node2);
                        }
                        Instance::I(e) => {
                            try_add_node(&e.node1);
                            try_add_node(&e.node2);
                        }
                    }
                }
                Statement::Command(_) => {}
            }
        }

        mapping
    }

    pub fn probe_nodes(&self) -> Vec<String> {
        let mut nodes = Vec::<String>::new();

        for statement in self.statements.iter() {
            match statement {
                Statement::Instance(_) => {},
                Statement::Command(cmd) => if let Command::Probe(node) = cmd {
                    nodes.push(node.to_string());
                }
            }
        }

        nodes
    }

    pub fn reduce_parallel_capacitance(&mut self) {
        use std::hash::{Hash, Hasher};
        
        #[derive(Debug, Clone)]
        struct TwoNodes {
            node1: String,
            node2: String,
        }

        impl PartialEq for TwoNodes {
            fn eq(&self, other: &Self) -> bool {
                (self.node1 == other.node1 && self.node2 == other.node2) || 
                (self.node1 == other.node2 && self.node2 == other.node1)
            }
        }

        impl Eq for TwoNodes {}
        
        impl Hash for TwoNodes {
            fn hash<H: Hasher>(&self, state: &mut H) {
                // 确保 hash 不受 node1 和 node2 顺序影响
                let mut nodes = [self.node1.clone(), self.node2.clone()];
                nodes.sort(); // 排序以确保一致性
                nodes[0].hash(state);
                nodes[1].hash(state);
            }
        }

        // Find all capacitances, and record their node
        let mut caps = HashMap::<TwoNodes, Vec<f64>>::new();
        for s in self.statements.iter() {
            if let Statement::Instance(i) = s {
                if let Instance::C(c) = i {
                    let node1 = c.node1.clone();
                    let node2 = c.node2.clone();
                    caps.entry(TwoNodes{node1, node2}).or_default().push(c.value);
                }
            }
        }

        // Remove all cap
        self.statements.retain(|s| !matches!(s, Statement::Instance(Instance::C(_))));

        // Add c
        for (i, (nodes, values)) in caps.into_iter().enumerate() {
            let value = values.into_iter().sum::<f64>();
            self.statements.push(Statement::Instance(Instance::C(Capacitance{
                name: format!("c{}", i),
                node1: nodes.node1, node2: nodes.node2,
                value
            })));
        }
    }
}

impl FromStr for Netlist {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut statements = Vec::new();
        for (row, line) in s.lines().enumerate() {
            match line.parse::<Statement>() {
                Ok(s) => statements.push(s),
                Err(err) => match err {
                    StatementErr::Empty => continue,
                    StatementErr::Err(err) => {
                        return Err(format!("In '{}' row: {}", row, err))
                    }
                }
            }
        }

        Ok(Self {
            statements
        })
    }
}

#[allow(unused)]
mod test {
    use super::*;

    #[test]
    fn test_from_str() {
        let s = 
            r#"vin n_in1  	0 		5
            rseg#x1 n_in1 n_1#x1 3
            lseg#x1 n_1#x1 n_in2 2
            cseg#x1 n_in2 0 4

            rseg#x2 n_in2 n_1#x2 3
            lseg#x2 n_1#x2 n_in3 2
            cseg#x2 n_in3 0 4

            rseg#x3 n_in3 n_1#x3 3
            lseg#x3 n_1#x3 n_out 2
            cseg#x3 n_out 0 4

            cl	n_out	0		2
            .probe  v(n_out)
            .end
            "#;
        assert!(Netlist::from_str(s).is_ok())
    }


    #[test]
    fn test_load_from_file() {
        let n = Netlist::load_from_file("./benchmark/RLC_s3.sp.expand.sp").unwrap();
    }

}

